package com.travelgroup.service.impl;
import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.travelgroup.constant.NotificationConstant;
import com.travelgroup.constant.RedisConstant;
import com.travelgroup.domain.dto.*;
import com.travelgroup.domain.entity.*;
import com.travelgroup.domain.vo.ItineraryVO;
import com.travelgroup.domain.vo.TourGroupVO;
import com.travelgroup.domain.vo.UserGroupVO;
import com.travelgroup.domain.vo.UserVO;
import com.travelgroup.enums.*;
import com.travelgroup.exception.BusinessException;
import com.travelgroup.exception.R;
import com.travelgroup.mapper.TourGroupMapper;
import com.travelgroup.service.*;
import com.travelgroup.utils.RedisUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class TourGroupServiceImpl extends ServiceImpl<TourGroupMapper, TourGroup>
        implements ITourGroupService {
    @Resource
    private IItineraryService itineraryService;
    @Resource
    private IUserGroupService userGroupService;
    @Resource
    private RedisUtil redisUtil;
    @Resource
    private INotificationService notificationService;
    @Resource
    private IUserService userService;
    @Resource
    private IPaymentOrderService paymentOrderService;

    /**
     * 发布拼团
     * @param tourGroup
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<Long> submitTourGroup(TourGroupDTO tourGroup) {
        // 保存组团信息
        if (tourGroup == null) {
            throw new BusinessException("参数不能为空");
        }
        TourGroup group = new TourGroup();
        BeanUtil.copyProperties(tourGroup, group);
        if (!this.save(group)) {
            throw new BusinessException("拼团信息存储失败");
        }

        // 保存总行程信息
        ItineraryDTO itineraryDTO = tourGroup.getItinerary();
        if (itineraryDTO == null) {
            throw new BusinessException("行程不能为空");
        }
        itineraryDTO.setGroupId(group.getId());

        itineraryService.saveItinerary(itineraryDTO);
        // 清除缓存
        redisUtil.removeById(RedisConstant.TOUR_GROUP_KEY, group.getId());

        // TODO 优惠券暂设为0
        // 将创建者作为团长加入组队中
        Long userId = StpUtil.getLoginIdAsLong();
        saveUserGroup(new UserGroupDTO(userId, group.getId(), 0L, RoleType.TEAM_LEADER));
        return R.success("拼团信息存储成功", group.getId());
    }

    /**
     * 根据id获取拼团信息
     * @param id
     * @return
     */
    @Override
    public R<TourGroupVO> queryTourGroupById(Long id) {
        // 获取拼团信息
        TourGroup group = getByRedis(id);
        if (group == null) {
            throw new BusinessException("拼团信息不存在");
        }
        TourGroupVO tourGroupVO = BeanUtil.copyProperties(group, TourGroupVO.class);

        // 获取行程信息
        ItineraryVO itineraryVO = itineraryService.queryItinerary(id);
        tourGroupVO.setItinerary(itineraryVO);
        return R.success("拼团信息查询成功", tourGroupVO);
    }

    /**
     * 取消拼团旅行
     * @param id
     * @return
     */
    @Override
    public R<Long> cancelTourGroup(Long id) {
        if (id == null) throw new BusinessException("id不能为空");
        TourGroup group = this.getById(id);

        if (group == null) {
            throw new BusinessException("拼团信息不存在");
        }

        // 判断是否符合取消条件
        if (group.getStatus() == GroupStatus.FORMED || group.getStatus() == GroupStatus.CANCEL) {
            throw new BusinessException("已成团无法取消");
        }

        // 更新拼团信息
        group.setStatus(GroupStatus.CANCEL);
        if (!this.updateById(group)) {
            throw new BusinessException("拼团信息更新失败");
        }

        // 进行批量退款
        List<PaymentOrder> paymentOrders = paymentOrderService.lambdaQuery()
                .eq(PaymentOrder::getStatus, PayStatus.SUCCESS)
                .eq(PaymentOrder::getGroupId, group.getId())
                .list();
        paymentOrderService.processBatchRefund(paymentOrders);

        // 退款后为参与用户发送退款信息
        Long organizer = StpUtil.getLoginIdAsLong();
        Itinerary itinerary = itineraryService.getByRedis(id);
        notificationService.sendCancellationNotifications(organizer, itinerary.getTitle(),
                paymentOrders, "取消拼团");

        // 取消后清除缓存
        redisUtil.removeById(RedisConstant.TOUR_GROUP_KEY, id);
        return R.success("成功取消拼团", id);
    }

    /**
     * 删除拼团信息
     * @param id
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<?> deleteTourGroup(Long id) {
        if (id == null) {
            throw new BusinessException("id不能为空");
        }
        if (!this.lambdaQuery().eq(TourGroup::getId, id).exists()) {
            throw new BusinessException("拼团信息不存在");
        }
        if (!this.removeById(id)) {
            throw new BusinessException("拼团信息删除失败");
        }

        itineraryService.deleteItinerary(id);
        redisUtil.removeById(RedisConstant.TOUR_GROUP_KEY, id);
        return R.success("拼团信息删除成功", null);
    }

    /**
     * 更新拼团信息
     * @param id
     * @param tourGroup
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public R<TourGroupDTO> updateTourGroupById(Long id, TourGroupDTO tourGroup) {
        if (id == null) {
            throw new BusinessException("id不能为空");
        }
        TourGroup group = this.getById(id);
        if (group == null) {
            throw new BusinessException("拼团信息不存在");
        }
        BeanUtil.copyProperties(tourGroup, group);
        if (!this.updateById(group)) {
            throw new BusinessException("拼团信息修改失败");
        }

        itineraryService.updateItinerary(id, tourGroup.getItinerary());
        redisUtil.removeById(RedisConstant.TOUR_GROUP_KEY, id);
        return R.success("拼团信息修改成功", tourGroup);
    }

    /**
     * 加入拼团队伍
     * @param userGroupDTO
     * @return
     */
    @Override
    @Transactional
    public R<Long> saveUserGroup(UserGroupDTO userGroupDTO) {
        log.info("拼团信息存储");
        Long groupId = userGroupDTO.getGroupId();
        // 获取拼团信息，检查是否满员或成团
        log.info("检查是否满员");
        if (isMaxMember(groupId)) {
            log.info("已成团");
            throw new BusinessException("已成团或已满员");
        }
        log.info("队伍未满员");

        // 存储组团数据
        Long userId = userGroupDTO.getUserId();
        UserGroup userGroup = new UserGroup();
        BeanUtils.copyProperties(userGroupDTO, userGroup);
        userGroup.setUserId(userId);
        userGroup.setRole(RoleType.REMEMBER);
        userGroup.setStatus(UserGroupStatus.NORMAL);
        userGroup.setJoinTime(LocalDateTime.now());
        log.info("存储拼团数据");
        if (!userGroupService.save(userGroup)) {
            log.info("参与失败");
            throw new BusinessException("参与拼团失败");
        }
        log.info("存储成功");
        redisUtil.removeById(RedisConstant.USER_GROUP_KEY + userId + ":", userGroupDTO.getGroupId());
        log.info("删除缓存成功");

        // 更新拼团信息
        TourGroup group = getByRedis(groupId);
        log.info("开始更新拼团信息");
        updateGroupStatus(group, 1);
        log.info("更新成功");
        return R.success("成功参与拼团",  userGroup.getId());
    }

    /**
     * 取消组队
     * @param groupId
     * @return
     */
    @Override
    @Transactional(rollbackFor = BusinessException.class)
    public R<?> cancelUserGroup(Long groupId) {
        // 获取用户id并查询组队信息
        Long userId = StpUtil.getLoginIdAsLong();

        // 检查用户是否已支付
        boolean isPaid = paymentOrderService.hasUserPaid(groupId, userId);

        // 更新组队状态为取消
        boolean isSuccess = userGroupService.lambdaUpdate()
                .eq(UserGroup::getGroupId, groupId)
                .eq(UserGroup::getUserId, userId)
                .eq(UserGroup::getStatus, GroupStatus.FORMED)
                .set(UserGroup::getStatus, UserGroupStatus.CANCEL)
                .update();
        if (!isSuccess) {
            throw new BusinessException("组队取消失败");
        }
        redisUtil.removeById(RedisConstant.USER_GROUP_KEY + userId + ":", groupId);

        // 检查取消后拼团信息的人数以及状态
        TourGroup group = getByRedis(groupId);
        updateGroupStatus(group, -1);

        // 退款
        if (isPaid) {
            boolean refundSuccess = paymentOrderService.refundByGroupAndUser(groupId, userId, "用户取消拼团");
            if (!refundSuccess) {
                log.warn("用户申请退款失败");
                throw new BusinessException("用户申请退款失败");
            }
        } else {
            throw new BusinessException("不存在支付订单");
        }

        // 发送退出通知
        User user = userService.getById(userId);
        List<Long> rememberIds = userGroupService.lambdaQuery()
                .eq(UserGroup::getGroupId, groupId)
                .list()
                .stream().map(UserGroup::getUserId)
                .toList();
        notificationService.sendNotification(NotificationConstant.CANCEL_TITLE,
                NotificationConstant.CANCEL_MESSAGE, group, user, rememberIds);

        return R.success("组队取消成功", null);
    }

    /**
     * 更新队伍状态
     * @param group
     * @param value
     */
    private void updateGroupStatus(TourGroup group, Integer value) {
        // 检查加入以及取消拼团后拼团信息的人数以及状态
        log.info("开始更新拼团人数");
        group.setCurrentMembers(group.getCurrentMembers() + value);
        if (group.getMaxMembers().equals(group.getCurrentMembers())) {
            log.info("拼团已满员");
            group.setStatus(GroupStatus.FORMED);
        } else {
            log.info("拼团招募中");
            group.setStatus(GroupStatus.RECRUITING);
        }
        log.info("开始更新数据");
        if (!this.updateById(group)) {
            log.info("更新失败");
            throw new BusinessException("拼团信息更新失败");
        }
        log.info("更新成功");
        redisUtil.removeById(RedisConstant.TOUR_GROUP_KEY, group.getId());
    }

    /**
     * 检查是否满员
     * @param id
     * @return
     */
    private boolean isMaxMember(Long id) {
        TourGroup group = getByRedis(id);
        if (group.getStatus() == GroupStatus.FORMED) {
            return true;
        } else if(group.getCurrentMembers().equals(group.getMaxMembers())) {
            // 满员但未成团修改为已成团
            group.setStatus(GroupStatus.FORMED);
            if (!this.updateById(group)) {
                log.info("更新失败");
                throw new BusinessException("拼团信息更新失败");
            }
            redisUtil.removeById(RedisConstant.TOUR_GROUP_KEY, id);
            return true;
        }
        return false;
    }

    /**
     * 获取组队信息
     * @param current
     * @param size
     * @return
     */
    @Override
    public R<List<UserGroupVO>> queryMyTourGroup(Integer current, Integer size) {
        // 获取用户全部的拼团信息
        Long userId = StpUtil.getLoginIdAsLong();
        List<UserGroup> userGroups = userGroupService.lambdaQuery()
                .eq(UserGroup::getUserId, userId)
                .eq(UserGroup::getStatus, UserGroupStatus.NORMAL)
                .page(new Page<>(current, size))
                .getRecords();
        if (userGroups == null || userGroups.isEmpty()) {
            return R.success("暂未进行拼团", List.of());
        }

        // 获取拼团列表
        List<UserGroupVO> userGroupVOS = new ArrayList<>();
        for (UserGroup userGroup : userGroups) {
            UserGroupVO userGroupVO = new UserGroupVO();
            BeanUtil.copyProperties(userGroup, userGroupVO);
            Long groupId = userGroup.getGroupId();

            // 获取行程中的信息
            Itinerary itinerary = itineraryService.getByRedis(groupId);
            BeanUtil.copyProperties(itinerary, userGroupVO);
            userGroupVOS.add(userGroupVO);
        }
        return R.success("获取拼团信息成功", userGroupVOS);
    }

    /**
     * id查询
     * @param id
     * @return
     */
    @Override
    public TourGroup getByRedis(Long id) {
        return redisUtil.queryById(RedisConstant.TOUR_GROUP_KEY, id, TourGroup.class,
                this::getById, RedisConstant.CACHE_TTL, TimeUnit.MINUTES);
    }

    /**
     * 根据队伍id获取用户
     * @param groupId
     * @return
     */
    @Override
    public R<List<UserVO>> queryUserByGroupId(Long groupId) {
        // 获取队伍信息
        List<UserGroup> userGroups = userGroupService.lambdaQuery()
                .eq(UserGroup::getStatus, UserGroupStatus.NORMAL)
                .eq(UserGroup::getGroupId, groupId)
                .list();

        // 获取用户信息
        List<UserVO> userVOS = new ArrayList<>();
        for (UserGroup userGroup : userGroups) {
            User user = userService.getById(userGroup.getUserId());
            UserVO userVO = new UserVO();
            BeanUtils.copyProperties(user, userVO);
            userVOS.add(userVO);
        }
        return R.success("获取参与拼团用户成功", userVOS);
    }

    /**
     * 入队付费
     * @param joinDTO
     * @return
     */
    @Override
    @Transactional
    public R<Map<String, Object>> payAndJoin(JoinDTO joinDTO) {
        // 获取支付方式
        PayType payType = joinDTO.getPayType();
        Long userId = StpUtil.getLoginIdAsLong();
        Long groupId = joinDTO.getGroupId();

        // 查询行程信息
        TourGroup group = getByRedis(groupId);
        Itinerary itinerary = itineraryService.getByRedis(groupId);
        if (group == null) throw new BusinessException("拼团不存在");

        // 检查是否满员
        if (isMaxMember(groupId)) {
            throw new BusinessException("已成团或已满员");
        }
        // 用户是否入队
        if (userGroupService.lambdaQuery()
                .eq(UserGroup::getGroupId, group.getId())
                .eq(UserGroup::getStatus, UserGroupStatus.NORMAL)
                .eq(UserGroup::getUserId, userId).exists()) {
            throw new BusinessException("用户已入队");
        }

        // 获取用户信息和费用
        User user = userService.getById(userId);
        BigDecimal amount = group.getCurrentPrice();

        // 创建支付订单
        PaymentOrder paymentOrder = paymentOrderService.createPaymentOrder(group, user, amount, payType).getData();
        Map<String, Object> map = paymentOrderService.getPaymentInfo(paymentOrder, itinerary.getTitle());
        return R.success("创建订单成功", map);
    }

    /**
     * 支付回调
     * @param params
     * @return
     */
    @Override
    @Transactional
    public R<?> paymentCallback(HttpServletRequest params) {
        // 获取商家订单号和平台订单号以及费用
        String orderNo = params.getParameter("out_trade_no");
        String tradeNo = params.getParameter("trade_no");
        BigDecimal amount = new BigDecimal(params.getParameter("total_amount"));

        // 对订单进行处理
        boolean success = paymentOrderService.handlePaymentCallback(tradeNo, orderNo, amount);
        if (!success) {
            throw new BusinessException("支付出现异常");
        }
        log.info("支付订单处理成功，开始查询订单详情");

        // 获取订单信息
        PaymentOrder paymentOrder = paymentOrderService.lambdaQuery().eq(PaymentOrder::getOrderNo, orderNo).one();
        if (paymentOrder == null) {
            log.error("支付订单不存在，订单号: {}", orderNo);
            throw new BusinessException("支付订单不存在");
        }

        // 将用户加入拼团队伍
        log.info("创建用户组，团ID: {}", paymentOrder.getGroupId());
        Long userId = paymentOrder.getUserId();
        Long groupId = paymentOrder.getGroupId();
        UserGroupDTO dto = new UserGroupDTO(userId, paymentOrder.getGroupId(), 0L, RoleType.REMEMBER);
        Long userGroupId = this.saveUserGroup(dto).getData();
        if (userGroupId == null) {
            log.info("拼团数据为空");
        }

        // 更新队伍数据
        TourGroup group = getByRedis(groupId);
        User user = userService.getById(userId);
        if (!userGroupService.lambdaUpdate()
                .eq(UserGroup::getGroupId, groupId)
                .eq(UserGroup::getUserId, userId)
                .set(UserGroup::getActualPayment, amount).update()) {
            throw new BusinessException("更新拼团数据失败");
        }

        // 发送入队通知
        List<Long> rememberIds = userGroupService.lambdaQuery()
                .eq(UserGroup::getGroupId, group.getId())
                .eq(UserGroup::getStatus, UserGroupStatus.NORMAL)
                .list()
                .stream().map(UserGroup::getUserId)
                .toList();
        log.info("发送加入通知");
        notificationService.sendNotification(NotificationConstant.JOIN_TITLE,
                NotificationConstant.JOIN_MESSAGE, group, user, rememberIds);
        notificationService.sendPaidNotification(NotificationConstant.PAY_SUCCESS,
                NotificationConstant.PAY_SUCCESS_MESSAGE, orderNo, group.getId(), user.getId());
        return R.success("支付回调执行成功", null);
    }
}
